#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
双均线策略实现
基于短期和长期移动平均线的交叉信号进行交易
"""

import asyncio
from typing import Dict, Any, Optional, List
from datetime import datetime

from tqsdk.tafunc import ma

from config.config_manager import get_config
from utils.logger import get_logger, trade_log, position_log
from utils.exceptions import StrategyError, ValidationError
from core.api_manager import get_api_manager
from core.base_classes import BaseDataProvider
from strategies.base_strategy import BaseStrategy, SignalType, StrategyManager


class DualMovingAverageStrategy(BaseStrategy):
    """双均线策略"""

    def __init__(self, symbol: str, config: Optional[Dict[str, Any]] = None):
        # 如果没有传入配置，使用默认配置
        if config is None:
            config = get_config()
            config = config.strategy.__dict__ if hasattr(config, 'strategy') else {}

        super().__init__(symbol, config)

        # 双均线策略特有参数
        self.short_period = config.get('short_period', 5)  # 短期均线周期
        self.long_period = config.get('long_period', 20)   # 长期均线周期
        self.volume = config.get('volume', 3)             # 交易手数

        # 策略状态
        self.short_ma = None
        self.long_ma = None
        self.last_short_ma = None
        self.last_long_ma = None

        # 数据管理器
        self.data_manager = None

        self.logger.info(f"双均线策略参数: 短期={self.short_period}, 长期={self.long_period}, 手数={self.volume}")

    async def _initialize_strategy(self):
        """初始化策略"""
        try:
            # 获取API管理器
            api_manager = get_api_manager()

            # 初始化数据管理器
            from data.data_manager import MarketDataManager
            market_data_manager = MarketDataManager()
            await market_data_manager.initialize(api_manager)
            self.data_manager = await market_data_manager.get_data_manager(self.symbol)

            # 获取初始K线数据
            kline_data = await self.data_manager.get_kline_data(
                period=get_config().data.kline_period,
                length=self.long_period + 10
            )

            if kline_data and 'data' in kline_data:
                self.logger.info(f"初始K线数据获取成功，共 {len(kline_data['data'])} 条")
            else:
                raise StrategyError("无法获取初始K线数据")

            # 订阅数据更新
            await self.data_manager.subscribe_quote_updates(self._on_quote_update)
            await self.data_manager.subscribe_kline_updates(self._on_kline_update)

            self.logger.info("双均线策略初始化完成")

        except Exception as e:
            self.logger.error(f"双均线策略初始化失败: {e}")
            raise StrategyError(f"策略初始化失败: {e}")

    async def _on_quote_update(self, data_type: str, data: Dict[str, Any]):
        """行情更新回调"""
        if data_type == 'quote_update':
            await self.on_quote_update(data)

    async def _on_kline_update(self, data_type: str, data: Dict[str, Any]):
        """K线更新回调"""
        if data_type == 'kline_update':
            await self.on_kline_update(data)

    async def on_quote_update(self, quote: Dict[str, Any]):
        """行情更新处理"""
        # 更新最新价格
        current_price = quote.get('last_price', 0)

        # 记录价格变化
        if current_price > 0:
            self.logger.debug(f"行情更新: {self.symbol} = {current_price}")

            # 检查是否需要触发止损或止盈
            if self.position != 0 and self.entry_price:
                await self._check_risk_management(current_price)

    async def on_kline_update(self, kline: Dict[str, Any]):
        """K线更新处理"""
        try:
            # 获取最新的K线数据进行均线计算
            kline_data = await self.data_manager.get_kline_data(
                period=get_config().data.kline_period,
                length=self.long_period + 5
            )

            if not kline_data or 'data' not in kline_data:
                return

            klines_df = kline_data['data']

            if len(klines_df) < self.long_period:
                return

            # 计算均线
            self.short_ma = ma(klines_df["close"], self.short_period)
            self.long_ma = ma(klines_df["close"], self.long_period)

            if len(self.short_ma) < 2 or len(self.long_ma) < 2:
                return

            # 获取最新的均线值
            current_short_ma = self.short_ma.iloc[-1]
            current_long_ma = self.long_ma.iloc[-1]
            prev_short_ma = self.short_ma.iloc[-2]
            prev_long_ma = self.long_ma.iloc[-2]

            self.logger.debug(f"均线计算: 短期={current_short_ma:.2f}, 长期={current_long_ma:.2f}")

            # 生成交易信号
            signal = await self.generate_signal()

            if signal and signal != SignalType.HOLD:
                await self.execute_signal(signal)

            # 更新上一次的均线值
            self.last_short_ma = current_short_ma
            self.last_long_ma = current_long_ma

        except Exception as e:
            self.logger.error(f"K线更新处理失败: {e}")

    async def generate_signal(self) -> Optional[SignalType]:
        """生成交易信号"""
        try:
            if self.short_ma is None or self.long_ma is None:
                return SignalType.HOLD

            if len(self.short_ma) < 2 or len(self.long_ma) < 2:
                return SignalType.HOLD

            current_short_ma = self.short_ma.iloc[-1]
            current_long_ma = self.long_ma.iloc[-1]
            prev_short_ma = self.short_ma.iloc[-2]
            prev_long_ma = self.long_ma.iloc[-2]

            # 金叉：短期均线上穿长期均线
            if (prev_short_ma <= prev_long_ma and
                current_short_ma > current_long_ma):
                self.logger.info(f"金叉信号: 短期{current_short_ma:.2f} 上穿 长期{current_long_ma:.2f}")
                return SignalType.BUY

            # 死叉：短期均线下穿长期均线
            elif (prev_short_ma >= prev_long_ma and
                  current_short_ma < current_long_ma):
                self.logger.info(f"死叉信号: 短期{current_short_ma:.2f} 下穿 长期{current_long_ma:.2f}")
                return SignalType.SELL

            return SignalType.HOLD

        except Exception as e:
            self.logger.error(f"生成信号失败: {e}")
            return SignalType.HOLD

    async def calculate_position_size(self) -> int:
        """计算仓位大小"""
        try:
            # 简单的固定仓位大小
            position_size = self.volume

            # 检查最大持仓限制
            if position_size > self.max_position:
                position_size = self.max_position

            if position_size < self.min_position:
                position_size = self.min_position

            return position_size

        except Exception as e:
            self.logger.error(f"计算仓位大小失败: {e}")
            return self.min_position

    async def _execute_trade(self, direction: str, volume: int) -> Dict[str, Any]:
        """执行交易"""
        try:
            # 获取当前价格
            quote = await self.data_manager.get_quote()
            current_price = quote.get('last_price', 0)

            if current_price <= 0:
                raise StrategyError("无法获取有效价格")

            # 记录交易前状态
            position_before = self.position

            # 执行交易（这里应该调用订单管理器，暂时模拟）
            trade_result = {
                'symbol': self.symbol,
                'direction': direction,
                'volume': volume,
                'price': current_price,
                'timestamp': datetime.now(),
                'order_id': f"sim_{datetime.now().strftime('%H%M%S%f')}",
                'status': 'completed'
            }

            # 更新持仓和成本
            if direction == 'BUY':
                if self.position < 0:  # 反手
                    self.position += volume
                else:
                    self.position += volume
                if self.position > 0:
                    self.entry_price = current_price
            else:  # SELL
                if self.position > 0:  # 反手
                    self.position -= volume
                else:
                    self.position -= volume
                if self.position < 0:
                    self.entry_price = current_price

            # 如果持仓为0，清空开仓价格
            if self.position == 0:
                self.entry_price = None

            # 记录交易
            trade_log(self.symbol, f"{direction}_OPEN", volume, current_price, trade_result['order_id'])
            position_log(self.symbol, self.position, None)

            self.logger.info(f"交易执行成功: {direction} {volume}手 @ {current_price}")

            return trade_result

        except Exception as e:
            self.logger.error(f"执行交易失败: {e}")
            raise StrategyError(f"执行交易失败: {e}")

    async def _check_risk_management(self, current_price: float):
        """检查风险管理"""
        try:
            if not self.entry_price:
                return

            # 获取风险管理器
            from risk.risk_manager import get_risk_manager
            risk_manager = get_risk_manager()

            # 检查持仓风险
            risk_info = await risk_manager.check_position_risk(
                self.symbol, self.position, self.entry_price, current_price
            )

            if risk_info.get('stop_loss_triggered') or risk_info.get('take_profit_triggered'):
                # 触发止损或止盈，平仓
                await self._close_position(f"风险管理: {risk_info.get('risk_level', 'unknown')}")

        except Exception as e:
            self.logger.error(f"风险管理检查失败: {e}")

    async def _close_position(self, reason: str):
        """平仓"""
        try:
            if self.position == 0:
                return

            direction = "SELL" if self.position > 0 else "BUY"
            volume = abs(self.position)

            self.logger.info(f"平仓: {reason}, {direction} {volume}手")

            # 执行平仓交易
            trade_result = await self._execute_trade(direction, volume)

            # 更新统计
            await self.update_statistics({
                'symbol': self.symbol,
                'pnl': 0,  # 这里需要计算实际盈亏
                'direction': direction,
                'volume': volume
            })

        except Exception as e:
            self.logger.error(f"平仓失败: {e}")

    def get_strategy_specific_info(self) -> Dict[str, Any]:
        """获取策略特定信息"""
        return {
            'strategy_type': 'dual_moving_average',
            'short_period': self.short_period,
            'long_period': self.long_period,
            'current_short_ma': self.short_ma.iloc[-1] if self.short_ma is not None and len(self.short_ma) > 0 else None,
            'current_long_ma': self.long_ma.iloc[-1] if self.long_ma is not None and len(self.long_ma) > 0 else None,
            'last_signal': self.last_signal.value if self.last_signal else None
        }

    def get_parameters(self) -> Dict[str, Any]:
        """获取策略参数"""
        return {
            'short_period': self.short_period,
            'long_period': self.long_period,
            'volume': self.volume,
            'max_position': self.max_position,
            'min_position': self.min_position
        }


async def create_dual_ma_strategy(symbol: str, config: Optional[Dict[str, Any]] = None) -> DualMovingAverageStrategy:
    """创建双均线策略实例"""
    strategy = DualMovingAverageStrategy(symbol, config)

    # 注册到策略管理器
    from strategies.base_strategy import get_strategy_manager
    strategy_manager = get_strategy_manager()
    await strategy_manager.register_strategy(strategy)

    return strategy


async def run_dual_ma_strategy(symbol: str = "SHFE.rb2401",
                              short_period: int = 5,
                              long_period: int = 20,
                              volume: int = 3):
    """运行双均线策略"""
    logger = get_logger("DualMAStrategy")

    try:
        # 配置策略参数
        config = {
            'name': 'dual_ma_strategy',
            'symbol': symbol,
            'short_period': short_period,
            'long_period': long_period,
            'volume': volume
        }

        # 创建并初始化策略
        strategy = await create_dual_ma_strategy(symbol, config)

        # 启动策略
        strategy_manager = get_strategy_manager()
        await strategy_manager.start_strategy('dual_ma_strategy', symbol)

        logger.info("双均线策略启动成功")
        logger.info(f"策略参数: 合约={symbol}, 短期均线={short_period}, 长期均线={long_period}, 手数={volume}")

        # 策略运行循环
        while strategy.is_running():
            await asyncio.sleep(1)  # 每秒检查一次状态

            # 获取策略状态
            status = strategy.get_status_info()
            if status['position'] != 0:
                logger.info(f"当前持仓: {status['position']}手, 开仓价格: {status['entry_price']}")

        logger.info("策略运行结束")

    except KeyboardInterrupt:
        logger.info("收到停止信号，正在停止策略...")
        if 'strategy' in locals():
            await strategy.stop()

    except Exception as e:
        logger.error(f"策略运行失败: {e}")
        raise


if __name__ == "__main__":
    # 示例运行
    asyncio.run(run_dual_ma_strategy(
        symbol="SHFE.rb2401",
        short_period=5,
        long_period=20,
        volume=3
    ))
